/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 *
 * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#include <mcamd_api.h>

/*
 * Indexed by syndrome, value is bit number.  If value is -1, a multi-bit
 * error has been detected.  A special case is the zero'th entry - a
 * syndrome of 0x0 indicates no bits in error.
 */
static char eccsynd[] = {
	-1, 64, 65, -1, 66, -1, -1, -1, 67, -1, -1, 17, -1, -1, 16, -1,
	68, -1, -1, 18, -1, 19, 20, -1, -1, 21, 22, -1, 23, -1, -1, -1,
	69, -1, -1,  8, -1,  9, 10, -1, -1, 11, 12, -1, 13, -1, -1, -1,
	-1, 14, -1, -1, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
	70, -1, -1, -1, -1, -1, -1, -1, -1, -1, 33, -1, -1, -1, -1, 32,
	-1, -1, 34, -1, 35, -1, -1, 36, 37, -1, -1, 38, -1, 39, -1, -1,
	-1, -1, 56, -1, 57, -1, -1, 58, 59, -1, -1, 60, -1, 61, -1, -1,
	62, -1, -1, -1, -1, 63, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
	71, -1, -1, -1, -1, -1, -1, -1, -1, -1, 49, -1, -1, -1, -1, 48,
	-1, -1, 50, -1, 51, -1, -1, 52, 53, -1, -1, 54, -1, 55, -1, -1,
	-1, -1, 40, -1, 41, -1, -1, 42, 43, -1, -1, 44, -1, 45, -1, -1,
	46, -1, -1, -1, -1, 47, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,  1, -1, -1,  0, -1,
	-1, -1, -1,  2, -1,  3,  4, -1, -1,  5,  6, -1,  7, -1, -1, -1,
	-1, -1, -1, 24, -1, 25, 26, -1, -1, 27, 28, -1, 29, -1, -1, -1,
	-1, 30, -1, -1, 31, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
};

/*
 * The first dimension of this table is the errored bit pattern, which is the
 * column dimension of the table in the BKDG.  Conveniently, the bit pattern
 * is also the lowest-order nibble in the syndrome, thus allowing the first
 * dimension value to be calculated.  The second dimension is the symbol
 * number, which can be found by searching for a matching syndrome.
 *
 * Note that the first dimension is actually (errored_bit_pattern - 1) since
 * 0 is not a valid errored bit pattern.
 */
#define	MCAMD_CKSYND_NPATS	15
#define	MCAMD_CKSYND_NSYMS	36

static uint16_t cksynd[MCAMD_CKSYND_NPATS][MCAMD_CKSYND_NSYMS] = {
	/* table column 0x1 */
	{ 0xe821, 0x5d31, 0x0001, 0x2021, 0x5041, 0xbe21, 0x4951, 0x74e1,
	0x15c1, 0x3d01, 0x9801, 0xd131, 0xe1d1, 0x6051, 0xa4c1, 0x11c1,
	0x45d1, 0x63e1, 0xb741, 0xdd41, 0x2bd1, 0x83c1, 0x8fd1, 0x4791,
	0x5781, 0xbf41, 0x9391, 0xcce1, 0xa761, 0xff61, 0x5451, 0x6fc1,
	0xbe01, 0x4101, 0xc441, 0x7621 },

	/* table column 0x2 */
	{ 0x7c32, 0xa612, 0x0002, 0x3032, 0xa082, 0xd732, 0x8ea2, 0x9872,
	0x2a42, 0x1602, 0xec02, 0x6212, 0x7262, 0xb0a2, 0xf842, 0x2242,
	0x8a62, 0xb172, 0xd982, 0x6682, 0x3d62, 0xc142, 0xc562, 0x89e2,
	0xa9c2, 0xd582, 0xe1e2, 0x4472, 0xf9b2, 0x55b2, 0xa8a2, 0xb542,
	0xd702, 0x8202, 0x4882, 0x9b32 },

	/* table column 0x3 */
	{ 0x9413, 0xfb23, 0x0003, 0x1013, 0xf0c3, 0x6913, 0xc7f3, 0xec93,
	0x3f83, 0x2b03, 0x7403, 0xb323, 0x93b3, 0xd0f3, 0x5c83, 0x3383,
	0xcfb3, 0xd293, 0x6ec3, 0xbbc3, 0x16b3, 0x4283, 0x4ab3, 0xce73,
	0xfe43, 0x6ac3, 0x7273, 0x8893, 0x5ed3, 0xaad3, 0xfcf3, 0xda83,
	0x6903, 0xc303, 0x8cc3, 0xed13 },

	/* table column 0x4 */
	{ 0xbb44, 0x9584, 0x0004, 0x4044, 0x9054, 0x2144, 0x5394, 0xd6b4,
	0xcef4, 0x8504, 0x6b04, 0x3884, 0xb834, 0x1094, 0xe6f4, 0xc8f4,
	0x5e34, 0x14b4, 0x2254, 0x3554, 0x4f34, 0xa4f4, 0xa934, 0x5264,
	0x92a4, 0x2954, 0x6464, 0xfdb4, 0xe214, 0x7914, 0x9694, 0x19f4,
	0x2104, 0x5804, 0xf654, 0xda44 },

	/* table column 0x5 */
	{ 0x5365, 0xc8b5, 0x0005, 0x6065, 0xc015, 0x9f65, 0x1ac5, 0xa255,
	0xdb35, 0xb805, 0xf305, 0xe9b5, 0x59e5, 0x70c5, 0x4235, 0xd935,
	0x1be5, 0x7755, 0x9515, 0xe815, 0x64e5, 0x2735, 0x26e5, 0x15f5,
	0xc525, 0x9615, 0xf7f5, 0x3155, 0x4575, 0x8675, 0xc2c5, 0x7635,
	0x9f05, 0x1905, 0x3215, 0xac65 },

	/* table column 0x6 */
	{ 0xc776, 0x3396, 0x0006, 0x7076, 0x30d6, 0xf676, 0xdd36, 0x4ec6,
	0xe4b6, 0x9306, 0x8706, 0x5a96, 0xca56, 0xa036, 0x1eb6, 0xeab6,
	0xd456, 0xa5c6, 0xfbd6, 0x53d6, 0x7256, 0x65b6, 0x6c56, 0xdb86,
	0x3b66, 0xfcd6, 0x8586, 0xb9c6, 0x1ba6, 0x2ca6, 0x3e36, 0xacb6,
	0xf606, 0xda06, 0xbed6, 0x4176 },

	/* table column 0x7 */
	{ 0x2f57, 0x6ea7, 0x0007, 0x5057, 0x6097, 0x4857, 0x9467, 0x3a27,
	0xf177, 0xae07, 0x1f07, 0x8ba7, 0x2b87, 0xc067, 0xba77, 0xfb77,
	0x9187, 0xc627, 0x4c97, 0x8e97, 0x5987, 0xe677, 0xe387, 0x9c17,
	0x6ce7, 0x4397, 0x1617, 0x7527, 0xbcc7, 0xd3c7, 0x6a67, 0xc377,
	0x4807, 0x9b07, 0x7a97, 0x3757 },

	/* table column 0x8 */
	{ 0xdd88, 0xeac8, 0x0008, 0x8088, 0xe0a8, 0x3288, 0xa1e8, 0x6bd8,
	0x4758, 0xca08, 0xbd08, 0x1cc8, 0xdc18, 0x20e8, 0x7b58, 0x4c58,
	0xa718, 0x28d8, 0x33a8, 0x1aa8, 0x8518, 0xf858, 0xfe18, 0xa3b8,
	0xe3f8, 0x3ea8, 0xb8b8, 0x56d8, 0x7328, 0x9e28, 0xebe8, 0x2e58,
	0x3208, 0xac08, 0x5ba8, 0x6f88 },

	/* table column 0x9 */
	{ 0x35a9, 0xb7f9, 0x0009, 0xa0a9, 0xb0e9, 0x8ca9, 0xe8b9, 0x1f39,
	0x5299, 0xf709, 0x2509, 0xcdf9, 0x3dc9, 0x40b9, 0xdf99, 0x5d99,
	0xe2c9, 0x4b39, 0x84e9, 0xc7e9, 0xaec9, 0x7b99, 0x71c9, 0xe429,
	0xb479, 0x81e9, 0x2b29, 0x9a39, 0xd449, 0x6149, 0xbfb9, 0x4199,
	0x8c09, 0xed09, 0x9fe9, 0x19a9 },

	/* table column 0xa */
	{ 0xa1ba, 0x4cda, 0x000a, 0xb0ba, 0x402a, 0xe5ba, 0x2f4a, 0xf3aa,
	0x6d1a, 0xdc0a, 0x510a, 0x7eda, 0xae7a, 0x904a, 0x831a, 0x6e1a,
	0x2d7a, 0x99aa, 0xea2a, 0x7c2a, 0xb87a, 0x391a, 0x3b7a, 0x2a5a,
	0x4a3a, 0xeb2a, 0x595a, 0x12aa, 0x8a9a, 0xcb9a, 0x434a, 0x9b1a,
	0xe50a, 0x2e0a, 0x132a, 0xf4ba },

	/* table column 0xb */
	{ 0x499b, 0x11eb, 0x000b, 0x909b, 0x106b, 0x5b9b, 0x661b, 0x874b,
	0x78db, 0xe10b, 0xc90b, 0xafeb, 0x4fab, 0xf01b, 0x27db, 0x7fdb,
	0x68ab, 0xfa4b, 0x5d6b, 0xa16b, 0x93ab, 0xbadb, 0xb4ab, 0x6dcb,
	0x1dbb, 0x546b, 0xcacb, 0xde4b, 0x2dfb, 0x34fb, 0x171b, 0xf4db,
	0x5b0b, 0x6f0b, 0xd76b, 0x829b },

	/* table column 0xc */
	{ 0x66cc, 0x7f4c, 0x000c, 0xc0cc, 0x70fc, 0x13cc, 0xf27c, 0xbd6c,
	0x89ac, 0x4f0c, 0xd60c, 0x244c, 0x642c, 0x307c, 0x9dac, 0x84ac,
	0xf92c, 0x3c6c, 0x11fc, 0x2ffc, 0xca2c, 0x5cac, 0x572c, 0xf1dc,
	0x715c, 0x17fc, 0xdcdc, 0xab6c, 0x913c, 0xe73c, 0x7d7c, 0x37ac,
	0x130c, 0xf40c, 0xadfc, 0xb5cc },

	/* table column 0xd */
	{ 0x8eed, 0x227d, 0x000d, 0xe0ed, 0x20bd, 0xaded, 0xbb2d, 0xc98d,
	0x9c6d, 0x720d, 0x4e0d, 0xf57d, 0x85fd, 0x502d, 0x396d, 0x956d,
	0xbcfd, 0x5f8d, 0xa6bd, 0xf2bd, 0xe1fd, 0xdf6d, 0xd8fd, 0xb64d,
	0x26dd, 0xa8bd, 0x4f4d, 0x678d, 0x365d, 0x185d, 0x292d, 0x586d,
	0xad0d, 0xb50d, 0x69bd, 0xc3ed },

	/* table column 0xe */
	{ 0x1afe, 0xd95e, 0x000e, 0xf0fe, 0xd07e, 0xc4fe, 0x7cde, 0x251e,
	0xa3ee, 0x590e, 0x3a0e, 0x465e, 0x164e, 0x80de, 0x65ee, 0xa6ee,
	0x734e, 0x8d1e, 0xc87e, 0x497e, 0xf74e, 0x9dee, 0x924e, 0x783e,
	0xd89e, 0xc27e, 0x3d3e, 0xef1e, 0x688e, 0xb28e, 0xd5de, 0x82ee,
	0xc40e, 0x760e, 0xe57e, 0x2efe },

	/* table column 0xf */
	{ 0xf2df, 0x846f, 0x000f, 0xd0df, 0x803f, 0x7adf, 0x358f, 0x51ff,
	0xb62f, 0x640f, 0xa20f, 0x976f, 0xf79f, 0xe08f, 0xc12f, 0xb72f,
	0x369f, 0xeeff, 0x7f3f, 0x943f, 0xdc9f, 0x1e2f, 0x1d9f, 0x3faf,
	0x8f1f, 0x7d3f, 0xaeaf, 0x23ff, 0xcfef, 0x4def, 0x818f, 0xed2f,
	0x7a0f, 0x370f, 0x213f, 0x58df }
};

int
mcamd_synd_validate(struct mcamd_hdl *hdl, uint32_t synd, int syndtype)
{
	int result;

	switch (syndtype) {
	case AMD_SYNDTYPE_ECC:
		result = (synd > 0 && synd <= 0xff);
		break;
	case AMD_SYNDTYPE_CHIPKILL:
		result = (synd > 0 && synd <= 0xffff);
		break;
	default:
		mcamd_dprintf(hdl, MCAMD_DBG_FLOW,
		    "mcamd_synd_validate: invalid syndtype %d\n", syndtype);
		return (0);
	}

	if (result == 0)
		mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "mcamd_synd_validate: "
		    "invalid %s syndrome 0x%x\n",
		    syndtype == AMD_SYNDTYPE_ECC ? "64/8" : "ChipKill",
		    synd);

	return (result);
}

int
mcamd_eccsynd_decode(struct mcamd_hdl *hdl, uint32_t synd, uint_t *bitp)
{
	char bit;

	if (synd > 0xff) {
		mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "mcamd_eccsynd_decode: "
		    "invalid synd 0x%x\n", synd);
		return (0);
	}
	if ((bit = eccsynd[synd]) == -1) {
		mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "mcamd_eccsynd_decode: "
		    "synd 0x%x is a multi-bit syndrome\n", synd);
		return (0);
	}

	mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "mcamd_eccsynd_decode: "
	    "synd 0x%x is single-bit and indicates %s bit %d\n", synd,
	    bit >= 64 ? "check" : "data",
	    bit >= 64 ? bit - 64 : bit);

	*bitp = bit;
	return (1);
}

int
mcamd_cksynd_decode(struct mcamd_hdl *hdl, uint32_t synd, uint_t *symp,
    uint_t *patp)
{
	int pat = synd & 0xf;
	int i;

	if (pat == 0) {
		mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "mcamd_cksynd_decode: "
		    "synd 0x%x is not a correctable syndrome\n", synd);
		return (0);
	}

	for (i = 0; i < MCAMD_CKSYND_NSYMS; i++) {
		if (cksynd[pat - 1][i] == synd) {
			*symp = i;
			*patp = pat;
			mcamd_dprintf(hdl, MCAMD_DBG_FLOW,
			    "mcamd_cksynd_decode: synd 0x%x is correctable "
			    "and indicates symbol %d\n", synd, i);
			return (1);
		}
	}

	mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "mcamd_cksynd_decode: "
	    "synd 0x%x is not a correctable syndrome\n", synd);
	return (0);
}

/*
 * symbols 0 to 0xf:		data[63:0]
 * symbols 0x10 to 0x1f:	data[127:64]
 * symbols 0x20, 0x21:		checkbits for [63:0]
 * symbols 0x22, 0x23:		checkbits for [127:64]
 */
/*ARGSUSED*/
int
mcamd_cksym_decode(struct mcamd_hdl *hdl, uint_t sym, int *lowbitp,
    int *hibitp, int *data, int *check)
{
	if (sym <= 0xf || sym >= 0x10 && sym <= 0x1f) {
		*data = 1;
		*check = 0;
		*lowbitp = sym * 4;
		*hibitp = (sym + 1) * 4 - 1;
	} else if (sym >= 0x20 && sym <= 0x23) {
		*data = 0;
		*check = 1;
		*lowbitp = (sym - 0x20) * 4;
		*hibitp = (sym + 1 - 0x20) * 4 - 1;
	} else {
		return (0);
	}

	return (1);
}
